//
// Copyright (C) [2024] Xingyun Integrated Circuit, Inc.
//
// GreenCode was a private technology asset of Xingyun Integrated Circuit， Inc （Confidential）
//  Author: Shawn.Tan
//  Date : 2025.10.28
//
//  History : Initial Version 2025.10.28
//

//
#include "RegisterGPGPU.h"

#include "Config.h"
#include "Constraint.h"
#include "ConstraintGPGPU.h"
#include "Log.h"
#include "Random.h"
#include "RegisterInitPolicyGPGPU.h"
#include "RegisterReserverGPGPU.h"
#include "UtilityFunctions.h"

using namespace std;

/*
  \file RegisterGPGPU.cc
  \brief Code for RegisterGPGPU class.
*/

namespace Green
{

  RegisterFileGPGPU::RegisterFileGPGPU()
    : RegisterFile()
  {
#ifndef UNIT_TEST
    AddInitPolicy(new PpnInitPolicy());
    AddInitPolicy(new VlInitPolicy());
    AddInitPolicy(new VstartInitPolicy());
    AddInitPolicy(new VtypeInitPolicy());
    AddInitPolicy(new VlmulInitPolicy());
#endif
  }

  RegisterFileGPGPU::RegisterFileGPGPU(const RegisterFileGPGPU& rOther)
    : RegisterFile(rOther)
  {

  }

  RegisterFileGPGPU::~RegisterFileGPGPU()
  {

  }

  Object * RegisterFileGPGPU::Clone() const
  {
    return new RegisterFileGPGPU(*this);
  }

  void RegisterFileGPGPU::SetupRegisterReserver()
  {
    mpRegisterReserver = new RegisterReserverGPGPU();
  }

  /*!
    Primary use case is called when about to write register initial value to ISS, and the underlying physical register is partially initialized since it is mapped to be multiple logical registers, some of which might be of smaller size.
    This method return the logical register that covers whole physical register, or could be multiple physical registers.
  */
  Register* RegisterFileGPGPU::GetContainingRegister(const Register* pReg) const
  {
    Register* containing_reg = nullptr;

    const string& reg_name = pReg->Name();
    if (reg_name[0] == 'S') {
      string reg_name_copy = reg_name;
      reg_name_copy[0] = 'D';
      containing_reg = RegisterLookup(reg_name_copy);
    } else if (reg_name[0] == 'H') {
      // half-precision...
      string reg_name_copy = reg_name;
      reg_name_copy[0] = 'D';
      containing_reg = RegisterLookup(reg_name_copy);
    }

    return containing_reg;
  }

  /*!
   *  If a register contains another register AND is otherwise uninitialized then it may be
   *  initialized (depending upon the registers in question) from the contained register.
   *
   *  The only case thusfar where this could be applicable is in the case of a GPGPU
   *  half-precision register...
   */

  bool RegisterFileGPGPU::InitContainingRegister(Register* rContainingReg, const Register* pReg) const 
  {
    if ((rContainingReg->Name()[0] == 'D') && ((pReg->Name()[0] == 'H') || (pReg->Name()[0] == 'S'))) {
        LOG(debug) << "{RegisterFileGPGPU::InitContainingRegister} initializing containing reg " << rContainingReg->Name()
	           << std::endl;
	InitializeRegister(rContainingReg->Name(), -1ull, NULL); // NaN boxing half-precision value
	return true;
    }
    return false;
  }

  bool RegisterFileGPGPU::AllowReExecutionInit(const std::string& rRegName) const
  {
    return false;
  }

  Register* RegisterFileGPGPU::RegisterLookupByIndex(uint32 index, const ERegisterType reg_type, uint32 size) const
  {
    char reg_prefix = 'x';
    char print_buffer[16];
    snprintf(print_buffer, 16, "%c%d", reg_prefix, index);

    return RegisterLookup(print_buffer);
  }

  uint32_t  RegisterFileGPGPU::GetWarpLimit() const 
  {
    const string &reg_warp_name = "TC_SOURCE_NWAR"; 
    Register* reg_ptr = RegisterLookup(reg_warp_name); 

    auto limit = reg_ptr->Value() & 0xF;
    return limit;
  }

  bool RegisterFileGPGPU::GetRandomRegistersForAccess(cuint32 number, const ERegisterType regType, const ERegAttrType access, const std::string& rExcludes, std::vector<uint64>& rRegIndices) const
  {
    auto limit = GetWarpLimit();
    ConstraintSetGPGPU reg_constr;
    reg_constr.SetWarpLimit(limit);
    mpRegisterReserver->UsableIndexConstraint(regType, access, &reg_constr);

    if (rExcludes.size() > 0) {
      ConstraintSet exclude_constr(rExcludes);
      reg_constr.SubConstraintSet(exclude_constr);
    }

    vector<uint64> value_list;
    reg_constr.GetValues(value_list);

    if (value_list.size() < number) {
      LOG(warn) << "{Generator::GetRandomRegisters} not enough " << ERegisterType_to_string(regType) << " random registers available, requesting: " << dec << number << " available: " << value_list.size() << endl;
      return false;
    }

    RandomURBG32 urbg32(Random::Instance());
    std::shuffle(value_list.begin(), value_list.end(), urbg32);

    rRegIndices.insert(rRegIndices.begin(), value_list.begin(), value_list.begin() + number);
    return true;
  }

}

